
// Main header
#include "Main.h"

// My header
#include "TextRender.h"

// Fridge
#include "MathsHelper.h"
#include "StringHash.h"
#include "TextureManager.h"
#include "Timer.h"

TTF_Font* TextRender::s_apxSDL_TTF_Handles[ iMAX_HANDLES ];
FONT TextRender::s_eCurrentFont = FONT_SANS_12;
u_int TextRender::s_uCurrentCol = 0xFFFFFFFF;
LineColourCallback TextRender::s_pfnCurrentLineColourCallback = 0;
char TextRender::s_szLineBuffer[];
static const int iMAX_LINE_LEN = 256;

void TextRender::Initialise	( )
{
	FridgeLog(("TextRender manager init\n"));

	if( TTF_Init() < 0 )
	{
		FridgeLog("Failed to init SDL_TTF\n");
		FAssert( false );
	}

	TextRender::LoadFont( FONT_SANS_12, "Fonts/Vera.ttf", 12 );
	TextRender::LoadFont( FONT_MONO_10, "Fonts/DejaVu.ttf", 12 );
}

void TextRender::Shutdown	( )
{
	for( int iFont = 0; iFont < iMAX_HANDLES; ++iFont )
	{
		if( s_apxSDL_TTF_Handles[ iFont ] )
		{
			TTF_CloseFont( s_apxSDL_TTF_Handles[ iFont ] );
		}
	}
	TTF_Quit();
}

void TextRender::LoadFont( int iHandle, char* szTypeface, int iSize )
{
	FridgeLogf("Loading font %s\n", szTypeface);

	s_apxSDL_TTF_Handles[ iHandle ] = TTF_OpenFont( szTypeface, iSize );

	FAssert( s_apxSDL_TTF_Handles[ iHandle ] != 0 );
}

TextTexture TextRender::PrintToTexture( const char* szText )
{
	SDL_Color xCol;
	xCol.r = Uint8(-1 );
	xCol.g = Uint8(-1 );
	xCol.b = Uint8(-1 );

	SDL_Surface *pxText = TTF_RenderText_Blended( s_apxSDL_TTF_Handles[ s_eCurrentFont ], szText, xCol );
	if(pxText)
	{
		// Generate a texture

		glEnable( GL_TEXTURE_2D );

		GLuint uTextureID;
		
		glGenTextures( 1, &uTextureID );

		glBindTexture( GL_TEXTURE_2D, uTextureID );

		glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR );
		glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR );

		glTexImage2D(	GL_TEXTURE_2D,
						0,
						GL_RGBA8, 
						pxText->w,
						pxText->h,
						0,
						GL_RGBA,
						GL_UNSIGNED_BYTE,
						pxText->pixels );
		
		glDisable( GL_TEXTURE_2D );

		return TextTexture( uTextureID, pxText );
	}

	return TextTexture();
}

void TextRender::Print( const char* szText, const u_int uX, const u_int uY )
{
	// Detect line breaks and print each line seperately

	int iLineStart = 0;
	int iLineEnd = -1;

	u_int uInternalY = uY;

	const int iLineSkip = TTF_FontLineSkip( s_apxSDL_TTF_Handles[ s_eCurrentFont ] );

	do
	{
		++iLineEnd;
		if( szText[ iLineEnd ] == '\r' || szText[ iLineEnd ] == 0 )
		{
			if( iLineStart != iLineEnd )
			{
				const int iLen = iLineEnd - iLineStart;
				memcpy( &s_szLineBuffer[0], &szText[ iLineStart ], MACRO_MIN( iLen, iMAX_LINE_LEN ) );
				s_szLineBuffer[ iLen ] = 0;
			
				PrintLine( &s_szLineBuffer[0], uX, uInternalY );
			}
		}
		else if( szText[ iLineEnd ] == '\n' )
		{
			iLineStart = iLineEnd + 1;
			uInternalY += static_cast< u_int >( iLineSkip );
		}
	}
	while( szText[ iLineEnd ] );
}

void TextRender::PrintLine( const char* szText, const u_int uX, const u_int uY )
{
	TextTexture xTexture = PrintToTexture( szText );

	if( !xTexture.IsValid() )
	{
		return;
	}

	u_int uWidth, uHeight;
	GetRenderSize( szText, uWidth, uHeight );

	glEnable( GL_TEXTURE_2D );

	glBindTexture( GL_TEXTURE_2D, xTexture.uGLTexture );

	glBegin( GL_QUADS );

	u_int uCol = s_uCurrentCol;
	if( s_pfnCurrentLineColourCallback )
	{
		uCol = s_pfnCurrentLineColourCallback( szText );
	}

	// uCol = MathsHelper::RGBAtoABGR( uCol );

	glColor4ub( ( uCol >> 24 ) & 0xFF, ( uCol >> 16 ) & 0xFF, ( uCol >> 8 ) & 0xFF, ( uCol ) & 0xFF );

    glTexCoord2f( 0.0f, 0.0f );
	glVertex2i( uX, uY );

	glTexCoord2f( 1.0f, 0.0f );
	glVertex2i( uX + uWidth, uY );

	glTexCoord2f( 1.0f, 1.0f );
	glVertex2i( uX + uWidth, uY + uHeight );

	glTexCoord2f( 0.0f, 1.0f );
	glVertex2i( uX, uY + uHeight );

	glEnd();

	xTexture.Free();
}

void TextRender::GetRenderSize( const char* szText, u_int& uWidth, u_int& uHeight )
{
	if( !s_apxSDL_TTF_Handles[ s_eCurrentFont ] )
	{
		return;
	}

	int iWidth = 0;
	int iHeight = 0;

	int iTempHeight = 0;
	int iTempWidth = 0;

	int iLineStart = 0;
	int iLineEnd = -1;

	// Detect line breaks and use TTF_FontLineSkip to calculate height
	
	const int iLineSkip = TTF_FontLineSkip( s_apxSDL_TTF_Handles[ s_eCurrentFont ] );
	iHeight = iLineSkip;
	
	do
	{
		++iLineEnd;
		if( szText[ iLineEnd ] == '\r' || szText[ iLineEnd ] == 0 )
		{
			if( iLineStart != iLineEnd )
			{
				const int iLen = iLineEnd - iLineStart;
				memcpy( &s_szLineBuffer[0], &szText[ iLineStart ], MACRO_MIN( iLen, iMAX_LINE_LEN ) );
				s_szLineBuffer[ iLen ] = 0;
			
				if( !TTF_SizeText( s_apxSDL_TTF_Handles[ s_eCurrentFont ], &s_szLineBuffer[0], &iTempWidth, &iTempHeight ) )
				{
					iWidth = MACRO_MAX( iWidth, iTempWidth );
				}
			}
		}
		else if( szText[ iLineEnd ] == '\n' )
		{
			iLineStart = iLineEnd + 1;
			iHeight += iLineSkip;
		}
	}
	while( szText[ iLineEnd ] );

	uWidth = static_cast< u_int >( iWidth );
	uHeight = static_cast< u_int >( iHeight ) + 1; // TY - Add 1 because, er... the height is always out by one :/
}

void TextRender::GetRenderPosOfChar( const char* szText, u_int uCharIndex, u_int& uX, u_int& uY )
{
	if( !s_apxSDL_TTF_Handles[ s_eCurrentFont ] )
	{
		return;
	}

	uX = 0;
	uY = 0;

	int iLineStart = 0;
	int iLineEnd = -1;

	// Detect line breaks and use TTF_FontLineSkip to calculate height
	
	const int iLineSkip = TTF_FontLineSkip( s_apxSDL_TTF_Handles[ s_eCurrentFont ] );
		
	do
	{
		++iLineEnd;
		if( iLineEnd == static_cast<int>( uCharIndex ) )
		{
			const int iLen = iLineEnd - iLineStart;
			memcpy( &s_szLineBuffer[0], &szText[ iLineStart ], MACRO_MIN( iLen, iMAX_LINE_LEN ) );
			s_szLineBuffer[ iLen ] = 0;
			
			int iTempHeight;
			int iTempWidth;
			if( !TTF_SizeText( s_apxSDL_TTF_Handles[ s_eCurrentFont ], &s_szLineBuffer[0], &iTempWidth, &iTempHeight ) )
			{
				uX = static_cast<int>( iTempWidth );
			}
			return;
		}
		if( szText[ iLineEnd ] == '\n' )
		{
			iLineStart = iLineEnd + 1;
			uY += static_cast< u_int >( iLineSkip );
		}
	}
	while( szText[ iLineEnd ] );

	// We didn't find the char

	uX = 0;
	uY = 0;
}

u_int TextRender::GetFontAscent()
{
	return static_cast< u_int >( TTF_FontAscent( s_apxSDL_TTF_Handles[ s_eCurrentFont ] ) );
}

u_int TextRender::GetCharAtPos( const char* szText, u_int uX, u_int uY )
{
	const int iLineSkip = TTF_FontLineSkip( s_apxSDL_TTF_Handles[ s_eCurrentFont ] );

	int iY = MACRO_MAX( static_cast<int>( uY ) - iLineSkip / 3, 0 ); // Fudge factor :/

	// Which line contains this pos
	int iLineToFind = iY / iLineSkip;

	int iLineStart = 0;
	int iLineEnd = -1;

	do
	{
		++iLineEnd;
		if( szText[ iLineEnd ] == '\r' || szText[ iLineEnd ] == 0 )
		{
			if( iLineToFind == 0 )
			{
				if( iLineStart != iLineEnd )
				{
					int iLen = iLineEnd - iLineStart;

					memcpy( &s_szLineBuffer[0], &szText[ iLineStart ], MACRO_MIN( iLen, iMAX_LINE_LEN ) );
					s_szLineBuffer[ iLen ] = 0;

					int iTempHeight = 0;
					int iTempWidth = 0;
					while( iLen )
					{
						TTF_SizeText( s_apxSDL_TTF_Handles[ s_eCurrentFont ], &s_szLineBuffer[0], &iTempWidth, &iTempHeight );
						if( static_cast<int>( uX ) > iTempWidth )
						{
							return iLineStart + iLen;
						}
						--iLen;
						s_szLineBuffer[iLen] = 0;
					}
				}
				return iLineStart;
			}
			--iLineToFind;
		}
		else if( szText[ iLineEnd ] == '\n' )
		{
			iLineStart = iLineEnd + 1;
		}
	}
	while( szText[ iLineEnd ] );

	return strlen( szText );
}

bool TextRender::FillLineBuffer( const char* szText, int iLine )
{
	s_szLineBuffer[0] = 0;

	int iLineStart = 0;
	int iLineEnd = -1;

	do
	{
		++iLineEnd;
		if( szText[ iLineEnd ] == '\r' || szText[ iLineEnd ] == 0 )
		{
			if( iLine == 0 )
			{
				if( iLineStart != iLineEnd )
				{
					const int iLen = iLineEnd - iLineStart;
					memcpy( &s_szLineBuffer[0], &szText[ iLineStart ], MACRO_MIN( iLen, iMAX_LINE_LEN ) );
					s_szLineBuffer[ iLen ] = 0;
				}
				return true;
			}
			--iLine;
		}
		else if( szText[ iLineEnd ] == '\n' )
		{
			iLineStart = iLineEnd + 1;
		}
	}
	while( szText[ iLineEnd ] );

	return false;
}

//eof

